package cn.lijiajia3515.cairo.auth.config.security;

import cn.lijiajia3515.cairo.auth.framework.security.oauth2.http.converter.CommonAuthorizationCodeTokenResponseClient;
import cn.lijiajia3515.cairo.auth.framework.security.user.CairoUserService;
import cn.lijiajia3515.cairo.auth.framework.security.web.authentication.CairoSimpleUrlAuthenticationFailureHandler;
import cn.lijiajia3515.cairo.auth.framework.security.web.configurers.CaptchaConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

	/**
	 * 认证服务器
	 *
	 * @param http security
	 * @return 认证服务器配置
	 * @throws Exception csrf 异常
	 */
	@Bean
	@Order(200)
	SecurityFilterChain cairoAuthorizationCustomSecurity(HttpSecurity http, ProviderSettings providerSettings) throws Exception {
		OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServer = new OAuth2AuthorizationServerConfigurer<>();

		http
			.requestMatcher(
				new OrRequestMatcher(
					new AntPathRequestMatcher("/.well-known/oauth-authorization-server", HttpMethod.GET.name()),
					new AntPathRequestMatcher(providerSettings.getTokenIntrospectionEndpoint(), HttpMethod.POST.name()),
					new AntPathRequestMatcher(providerSettings.getTokenRevocationEndpoint(), HttpMethod.POST.name()),
					new AntPathRequestMatcher(providerSettings.getTokenEndpoint(), HttpMethod.POST.name()),
					new AntPathRequestMatcher("/oauth2/password_code", HttpMethod.POST.name())
				)
			)

			.authorizeRequests(authorizeRequests ->
				authorizeRequests.anyRequest().permitAll()
			)

			.csrf(c -> c.ignoringAntMatchers("/**"))

			.cors(c -> {
			})

			.apply(authorizationServer)
			.and()

			.sessionManagement(c -> {
				c.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
			})

			.exceptionHandling(c -> {

			});

		return http.build();
	}

	public static final String REMEMBER_ME_PARAMETER_NAME = "remember_me";
	public static final String REMEMBER_ME_COOKIE_NAME = "remember_me";

	/**
	 * 认证服务器
	 *
	 * @param http security
	 * @return 认证服务器配置
	 * @throws Exception csrf 异常
	 */
	@Bean
	@Order(300)
	SecurityFilterChain cairoAuthorizationSecurity(HttpSecurity http, CairoUserService userService, PersistentTokenRepository persistentTokenRepository) throws Exception {
		CaptchaConfigurer<HttpSecurity> captchaConfigurer = new CaptchaConfigurer<>();
		captchaConfigurer.verifyCaptchaCodeRequestMatcher(
			new OrRequestMatcher(
				new AntPathRequestMatcher("/login/password", HttpMethod.POST.name()),
				new AntPathRequestMatcher("/test")
			)
		);
		OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServer = new OAuth2AuthorizationServerConfigurer<>();

		http
			.antMatcher("/**")
			.authorizeRequests(c -> c.anyRequest().permitAll())

			.csrf().disable()
			// .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))

			.cors().and()

			// 图形校验码
			.apply(captchaConfigurer)
			.and()

			.logout(c -> c
				.logoutUrl("/logout").permitAll()
				.deleteCookies(REMEMBER_ME_COOKIE_NAME)
			)

			.formLogin(c -> c
				.loginProcessingUrl("/login/password").permitAll()
				.loginPage("/login").permitAll()
				.failureHandler(new CairoSimpleUrlAuthenticationFailureHandler("/login?error"))

			)

			.oauth2Login(x -> x
				.loginProcessingUrl("/login/oauth2/code/*").permitAll()
				.loginPage("/login").permitAll()
				.tokenEndpoint(c -> c
					.accessTokenResponseClient(new CommonAuthorizationCodeTokenResponseClient()))
			)

			// 授权服务器
			.apply(authorizationServer)
			.and()

			.rememberMe(c -> c.
				rememberMeParameter(REMEMBER_ME_PARAMETER_NAME)
				.rememberMeCookieName(REMEMBER_ME_COOKIE_NAME)
				.tokenRepository(persistentTokenRepository)
				.userDetailsService(userService)
			)

			.sessionManagement(c -> c.
				sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 需要才创建, 一般只有登录使用
			)

			.exceptionHandling(c -> {

			});
		return http.build();
	}

	/**
	 * 路由白名单设置
	 *
	 * @return security config
	 */
	@Bean
	WebSecurityCustomizer webSecurityCustomizer() {
		return (web) -> web
			.ignoring()
			.antMatchers(HttpMethod.OPTIONS, "/**")
			.antMatchers("/actuator/**", "/favicon.ico", "**/favicon.ico", "/static/**", "/resources/**", "/public/**")
			// .antMatchers("/oauth2/password_code")
			;
	}

}
